package cn.buk.tms.dao;

import cn.buk.common.sc.CommonSearchCriteria;
import cn.buk.common.sc.Page;

import java.util.ArrayList;
import java.util.List;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import jakarta.persistence.criteria.*;
import jakarta.transaction.Transactional;

import cn.buk.common.util.DateUtil;
import cn.buk.tms.entity.base.Airline;
import cn.buk.tms.entity.base.Airport;
import cn.buk.tms.entity.base.City;
import cn.buk.tms.entity.misc.*;
import org.springframework.stereotype.Component;

import static cn.buk.tms.common.constant.TmsOrderConstant.OPERATING_OBJECT_BASIC_DATA;

/**
 * Created by yfdai on 2015-3-19.
 */
@Component
@Transactional
public class BasicDataDaoImpl extends AbstractDao implements BasicDataDao {

    private Airport getAirport(String threeCode, EntityManager em) {
        if (threeCode == null) {
          return null;
        }
        Airport port = null;

        List<Airport> list = em.createQuery("select u from Airport u where u.threeCode = ?1", Airport.class)
                .setParameter(1, threeCode).getResultList();

        if (list != null && list.size() > 0) {
            port = list.get(0);
        }
        return port;

    }


    @Override
    public Airport getAirport(String threeCode) {
        if (threeCode == null) {
          return null;
        }

        return getAirport(threeCode, em);
    }


    @Override
    @SuppressWarnings("unchecked")
    public boolean saveAirport(Airport airport) {
        boolean b = false;
        if (airport == null) {
          return false;
        }

        List<Airport> list = em.createQuery("select u from Airport u where u.threeCode = ?1")
                .setParameter(1, airport.getThreeCode()).getResultList();


        if (list.size() > 0) {
            Airport airport0 = list.get(0);

            if (airport0.getNameCN() == null || airport0.getNameCN().trim().length() == 0) {
              airport0.setNameCN(airport.getNameCN());
            }
            if (airport0.getCityCode() == null || airport0.getCityCode().trim().length() == 0) {
              airport0.setCityCode(airport.getCityCode());
            }
            if (airport0.getCountryCode() == null || airport0.getCountryCode().trim().length() == 0) {
              airport0.setCountryCode(airport.getCountryCode());
            }

            em.merge(airport0);
        } else {
          em.persist(airport);
        }

        b = true;


        return b;
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<Airport> searchAirport(String threeCode) {
        List<Airport> portList = null;

        Query query = null;
        if (threeCode == null || threeCode.length() != 3) {
            query = em.createQuery("select f from Airport f "
                    + "  order by f.threeCode");
        } else {
            query = em.createQuery("select f from Airport f "
                    + " where f.threeCode = ?1 "
                    + "  order by f.threeCode")
                    .setParameter(1, threeCode);
        }
        portList = query.getResultList();

        return portList;
    }

    @Override
    public List<Airport> searchAirportsByCountry(String countryCode) {
        return em.createQuery("select o from Airport o where o.countryCode = :countryCode order by o.threeCode", Airport.class)
                .setParameter("countryCode", countryCode)
                .getResultList();
    }

    private List<Predicate> buildPredicates_searchAirports(CriteriaBuilder cb, Root<Airport> root, CommonSearchCriteria sc) {
        List<Predicate> predicates = new ArrayList<>();

        Predicate where = cb.conjunction();
        predicates.add(where);

        if (sc.getCarrier() != null && sc.getCarrier().trim().length() == 2) {
            where = cb.equal(root.get("countryCode"), sc.getCarrier().trim().toUpperCase());
            predicates.add(where);
        }

        if (sc.getDport() != null && sc.getDport().trim().length() == 3) {
            where = cb.equal(root.get("threeCode"), sc.getDport().trim().toUpperCase());
            predicates.add(where);
        }

        if (sc.getName() != null && sc.getName().trim().length() > 0) {
            where = cb.like(root.get("cityName"), "%" + sc.getName().trim() + "%");
            where = cb.or(where, cb.like(root.get("nameCN"), "%" + sc.getName().trim() + "%"));
            predicates.add(where);
        }

        return predicates;
    }

    @Override
    public List<Airport> searchAirports(CommonSearchCriteria sc) {
        List<Airport> results = null;

        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Airport> cq = cb.createQuery(Airport.class);
        Root<Airport> root = cq.from(Airport.class);
        root.alias("o");

        var predicates0 = buildPredicates_searchAirports(cb, root, sc);



        cq.where(predicates0.toArray(new Predicate[predicates0.size()]));
        List<Order> orderByes = new ArrayList<>();
        orderByes.add(cb.asc(root.get("threeCode")));

        cq.orderBy(orderByes);

        //计算根据条件查询得出的数据总数
        CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
        Root<Airport> countRoot = countQuery.from(Airport.class);
        countRoot.alias("o");

        var predicates1 = buildPredicates_searchAirports(cb, countRoot, sc);

        countQuery.select(cb.count(countRoot)).where(predicates1.toArray(new Predicate[predicates1.size()]));
        Long count = em.createQuery(countQuery).getSingleResult();

        int maxResults = count.intValue();
        if (maxResults > 0) {
            Page page = sc.getPage();
            page.setRowCount(maxResults);

            results = em.createQuery(cq)
                    .setFirstResult(page.getFirstPosition())
                    .setMaxResults(page.getPageSize()).getResultList();
        }

        if (results == null) {
          results = new ArrayList<>();
        }

        return results;
    }

    @Override
    public List<Airport> getAllAirports() {
        return em.createQuery("select o from Airport o order by o.threeCode", Airport.class)
                .getResultList();
    }

    @Override
    public List<Airline> getAllAirlines() {
            return em.createQuery("select o from Airline o order by o.twoCode", Airline.class)
                    .getResultList();
    }

    @Override
    public List<Airline> searchAirlines(String twoCode) {
            return em.createQuery("select o from Airline o where o.twoCode = :twoCode order by o.twoCode", Airline.class)
                    .setParameter("twoCode", twoCode)
                    .getResultList();
    }

    @Override
    public List<Airline> searchAirlines(CommonSearchCriteria sc) {
        List<Airline> results = null;

        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Airline> cq = cb.createQuery(Airline.class);
        Root<Airline> root = cq.from(Airline.class);
        root.alias("o");

        var predicates0 = buildPredicates_searchAirlines(cb, root, sc);


        cq.where(predicates0.toArray(new Predicate[predicates0.size()]));
        List<Order> orderByes = new ArrayList<>();
        orderByes.add(cb.asc(root.get("twoCode")));

        cq.orderBy(orderByes);

        //计算根据条件查询得出的数据总数
        CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
        Root<Airline> countRoot = countQuery.from(Airline.class);
        countRoot.alias("o");

        var predicates1 = buildPredicates_searchAirlines(cb, root, sc);


        countQuery.select(cb.count(countRoot)).where(predicates1.toArray(new Predicate[predicates1.size()]));
        Long count = em.createQuery(countQuery).getSingleResult();

        int maxResults = count.intValue();
        if (maxResults > 0) {
            Page page = sc.getPage();
            page.setRowCount(maxResults);

            results = em.createQuery(cq)
                    .setFirstResult(page.getFirstPosition())
                    .setMaxResults(page.getPageSize()).getResultList();

        }

        if (results == null) {
          results = new ArrayList<>();
        }

        return results;
    }

    private List<Predicate> buildPredicates_searchAirlines(CriteriaBuilder cb, Root<Airline> root, CommonSearchCriteria sc) {
        List<Predicate> predicates = new ArrayList<>();

        Predicate where = cb.conjunction();
        predicates.add(where);

        if (sc.getCarrier() != null && sc.getCarrier().trim().length() == 2) {
            where = cb.equal(root.get("twoCode"), sc.getCarrier().trim().toUpperCase());
            predicates.add(where);
        }

        if (sc.getDport() != null && sc.getDport().trim().length() == 3) {
            where = cb.equal(root.get("threeCode"), sc.getDport().trim().toUpperCase());
            predicates.add(where);
        }
        return predicates;
    }

    @Override
    public int saveAirline(Airline airline) {
        int retCode = 0;

        if (airline.getId() > 0) {

            em.merge(airline);
        } else {
            em.persist(airline);
        }

        retCode = airline.getId();

        return retCode;
    }

    @Override
    public List<FareCondition> searchFareCondition(int enterpriseId, String carrier, String subclass) {
            return em.createQuery("select o from FareCondition o where o.carrier = :carrier and o.subclass = :subclass", FareCondition.class)
                    .setParameter("carrier", carrier)
                    .setParameter("subclass", subclass)
                    .getResultList();
    }

    @Override
    public int createAirport(Airport airport) {
        em.persist(airport);
        return 1;
    }

    @Override
    public int updateAirport(Airport airport) {
        airport.setLastUpdate(DateUtil.getCurDateTime());
        em.merge(airport);
        return 1;
    }

    @Override
    public int createAirlines(Airline airline) {
        em.persist(airline);
        return airline.getId();
    }

    @Override
    public int updateAirlines(Airline airlines) {
        airlines.setLastUpdate(DateUtil.getCurDateTime());
        em.merge(airlines);
        return airlines.getId();
    }

    @Override
  public List<SubclassInfo> searchSubclasses(String carrier, String subclass) {
      return em.createQuery("select o from SubclassInfo o where o.carrier = :carrier and o.subclass = :subclass", SubclassInfo.class)
              .setParameter("carrier", carrier)
              .setParameter("subclass", subclass)
              .getResultList();
  }

    @Override
    public List<SubclassInfo> searchSubclassInfos(int enterpriseId, String carrier) {
        return em.createQuery("select o from SubclassInfo o " +
                "where o.enterpriseId = :enterpriseId " +
                "and o.carrier = :carrier " +
                "order by o.cabinType, o.discount desc", SubclassInfo.class)
                .setParameter("enterpriseId", enterpriseId)
                .setParameter("carrier", carrier)
                .getResultList();
    }

    @Override
  public List<City> searchCities(CommonSearchCriteria sc) {
      List<City> results = null;

      CriteriaBuilder cb = em.getCriteriaBuilder();
      CriteriaQuery<City> cq = cb.createQuery(City.class);
      Root<City> root = cq.from(City.class);
      root.alias("o");

       var predicates0 = buildPredicates_searchCities(cb, root, sc);


        cq.where(predicates0.toArray(new Predicate[predicates0.size()]));
      List<Order> orderByes = new ArrayList<>();
      orderByes.add(cb.asc(root.get("id")));

      cq.orderBy(orderByes);

      //计算根据条件查询得出的数据总数
      CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
      Root<City> countRoot = countQuery.from(City.class);
      countRoot.alias("o");

        var predicates1 = buildPredicates_searchCities(cb, countRoot, sc);


      countQuery.select(cb.count(countRoot)).where(predicates1.toArray(new Predicate[predicates1.size()]));
      Long count = em.createQuery(countQuery).getSingleResult();

      int maxResults = count.intValue();
      if (maxResults > 0) {
          Page page = sc.getPage();
          page.setRowCount(maxResults);

          results = em.createQuery(cq)
                  .setFirstResult(page.getFirstPosition())
                  .setMaxResults(page.getPageSize()).getResultList();
      }

      if (results == null) {
          results = new ArrayList<>();
      }

      return results;
  }

    private List<Predicate> buildPredicates_searchCities(CriteriaBuilder cb, Root<City> root, CommonSearchCriteria sc) {
        List<Predicate> predicates = new ArrayList<>();

        Predicate where = cb.conjunction();
        predicates.add(where);

        if (sc.getCarrier() != null && sc.getCarrier().trim().length() == 2) {
            where = cb.equal(root.get("countryCode"), sc.getCarrier().trim().toUpperCase());
            predicates.add(where);
        }


        if (sc.getName() != null && sc.getName().trim().length() > 0) {
            where = cb.like(root.get("cityName"), "%" + sc.getName().trim() + "%");
            where = cb.or(where, cb.like(root.get("cityEnglishName"), "%" + sc.getName().trim() + "%"));
            predicates.add(where);
        }
        return predicates;
    }

    @Override
    public int createCity(final int enterpriseId, final String username, City city) {
        if (city.getId() == 0) {
            em.persist(city);
        } else {
            city.setLastUpdate(DateUtil.getCurDateTime());
            em.merge(city);
        }

        OperationLog log = OperationLog.createLog(enterpriseId, OPERATING_OBJECT_BASIC_DATA);
        log.setOperator(username);
        log.setContent("城市信息：" + city.getCityName());
        em.persist(log);

        return city.getId();
    }

  @Override
  public int saveFlightConditionTimeWindows(int userId, String carrier, List<FlightConditionTimeWindow> timeWindows) {
      User user = em.find(User.class, userId);

       em.createQuery("delete from FlightConditionTimeWindow o where o.carrier = :carrier")
              .setParameter("carrier", carrier)
              .executeUpdate();

      StringBuilder content = new StringBuilder();
      for (FlightConditionTimeWindow timeWindow : timeWindows) {
          timeWindow.setCarrier(carrier);
          em.persist(timeWindow);

          content.append(timeWindow.toString() + ";");
      }

      OperationLog log = OperationLog.createLog(user.getEnterpriseId(), OPERATING_OBJECT_BASIC_DATA);
      log.setOperator(user.getUsername());
      log.setContent("保存航司退改时间点信息：" + content.toString());
      em.persist(log);


      return 1;
  }

  @Override
  public int saveLuggageInfo(LuggageInfo info) {
      if (info.getId() > 0) {
          info.setLastUpdate(DateUtil.getCurDateTime());
          em.merge(info);
      } else {
          em.persist(info);
      }

      return info.getId();
  }

  @Override
  public List<LuggageInfo> searchLuggageInfos(int enterpriseId, CommonSearchCriteria sc) {
      List<LuggageInfo> results = null;

      CriteriaBuilder cb = em.getCriteriaBuilder();
      CriteriaQuery<LuggageInfo> cq = cb.createQuery(LuggageInfo.class);
      Root<LuggageInfo> root = cq.from(LuggageInfo.class);
      root.alias("h");

      var predicates0 = searchPredicates_searchLuggageInfos(enterpriseId, sc, cb, root);


      //计算根据条件查询得出的数据总数
      CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
      Root<LuggageInfo> countRoot = countQuery.from(LuggageInfo.class);
      countRoot.alias("h");

      var predicates1 = searchPredicates_searchLuggageInfos(enterpriseId, sc, cb, countRoot);

      // count items
      countQuery.select(cb.count(countRoot)).where(predicates1.toArray(new Predicate[predicates1.size()]));
      Long count = em.createQuery(countQuery).getSingleResult();

      //查询
      cq.select(root).where(predicates0.toArray(new Predicate[predicates0.size()]));

      List<Order> orderByes = new ArrayList<>();
      orderByes.add(cb.asc(root.get("luggageType")));
      orderByes.add(cb.asc(root.get("cabinType")));

      cq.orderBy(orderByes);


      int maxResults = count.intValue();
      if (maxResults > 0) {
          Page page = sc.getPage();
          page.setRowCount(maxResults);
          int i = (page.getPageNo() - 1) * page.getPageSize();

          if (i < 0) {
              i = 0;
          }


          results = em.createQuery(cq)
                  .setFirstResult(i)
                  .setMaxResults(page.getPageSize()).getResultList();

      }

      if (results == null) {
          results = new ArrayList<>();
      }

      return results;
  }

    private static List<Predicate> searchPredicates_searchLuggageInfos(int enterpriseId, CommonSearchCriteria sc, CriteriaBuilder cb, Root<LuggageInfo> root) {
        List<Predicate> predicates = new ArrayList<>();

        Predicate predicate = cb.conjunction();
        predicates.add(predicate);

        predicate = cb.equal(root.get(LuggageInfo_.enterpriseId), enterpriseId);
        predicates.add(predicate);

        if (sc.getCarrier().length() == 2) {
            predicate = cb.equal(root.get(LuggageInfo_.carrier), sc.getCarrier());
            predicates.add(predicate);
        }
        return predicates;
    }

    @Override
  public List<LuggageInfo> searchLuggageInfos(int enterpriseId, String twoCode) {
    return em.createQuery("select o from LuggageInfo o " +
            "where o.enterpriseId = :enterpriseId " +
            "and o.carrier = :twoCode " +
            "order by o.luggageType, o.cabinType", LuggageInfo.class)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("twoCode", twoCode)
            .getResultList();
  }

  @Override
  public LuggageInfo getLuggageInfoById(int id) {
        LuggageInfo info = em.find(LuggageInfo.class, id);
        if (info != null) {
            em.refresh(info);
        }
    return info;
  }

  @Override
  public List<FlightConditionTimeWindow> searchFlightConditionTimeWindows(String carrier) {
      return em.createQuery("select o from FlightConditionTimeWindow o " +
              "where o.carrier = :carrier order by o.windowOrder", FlightConditionTimeWindow.class)
              .setParameter("carrier", carrier)
              .getResultList();
  }

}
